package smartgrid.agents.regulator;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import smartgrid.agents.AbstractAgent;
import smartgrid.conf.RegulatorConf;
import smartgrid.modules.normative.sanction.DenounceSanction;
import smartgrid.modules.normative.sanction.PunishmentInformationSanction;
import smartgrid.modules.normative.sanction.PunishmentSanction;
import smartgrid.modules.normative.sanction.SanctionAbstract;
import smartgrid.modules.normative.sanction.SuspensionSanction;
import smartgrid.output.AbstractEntity;
import smartgrid.output.OutputController;
import smartgrid.output.AbstractEntity.EntityType;
import smartgrid.output.RegulatorOutputEntity;
import smartgrid.output.RegulatorOutputEntity.Field;
import smartgrid.util.random.RandomUtil;

public class RegulatorAgent extends AbstractAgent implements IRegulatorAgent {
	
	private int											numberDenouncesPunish;
	
	private double									probabilityPunish;
	
	private double									punishment;
	
	private int											numberDenouncesSuspension;
	
	private double									probabilitySuspension;
	
	private int											periodSuspension;
	
	private Map<Integer, Integer>		historic;
	
	private List<SanctionAbstract>	sanctions;
	
	
	/**
	 * Constructor
	 * 
	 * @param id
	 *          Regulator identification
	 * @param conf
	 *          Regulator configuration
	 * @return none
	 */
	public RegulatorAgent(Integer id, RegulatorConf conf) {
		super(id);
		
		this.numberDenouncesPunish = conf.getNumberDenouncesPunish();
		this.probabilityPunish = conf.getProbabilityPunish();
		this.punishment = conf.getPunishment();
		this.numberDenouncesSuspension = conf.getNumberDenouncesSuspension();
		this.probabilitySuspension = conf.getProbabilitySuspension();
		this.periodSuspension = conf.getPeriodSuspension();
		
		this.historic = new HashMap<Integer, Integer>();
		this.sanctions = new ArrayList<SanctionAbstract>();
	}
	
	
	@Override
	public void initTimestep(int timestep) {
		this.setTimestep(timestep);
		
		this.sanctions.clear();
	}
	
	
	@Override
	public List<SanctionAbstract> decideSanction(
			Map<Integer, List<SanctionAbstract>> sanctions) {
		
		for(Integer prosumerId : sanctions.keySet()) {
			List<SanctionAbstract> sanctionList = sanctions.get(prosumerId);
			
			for(SanctionAbstract sanction : sanctionList) {
				
				if (sanction.getType().equals(SanctionAbstract.Type.DENOUNCE)) {
					int numDenounces = 0;
					if (this.historic.containsKey(prosumerId)) {
						numDenounces = this.historic.get(prosumerId);
					}
					numDenounces++;
					this.historic.put(prosumerId, numDenounces);
					
					// Probability to suspend the Prosumer
					if (numDenounces > this.numberDenouncesSuspension) {
						if (RandomUtil.nextDouble() < this.probabilitySuspension) {
							SuspensionSanction suspension = new SuspensionSanction(this.id,
									prosumerId, this.periodSuspension);
							this.sanctions.add(suspension);
							numDenounces = 0;
							this.historic.put(prosumerId, numDenounces);
						}
					}
					
					// Probability to punish the Prosumer
					if (numDenounces > this.numberDenouncesPunish) {
						if (RandomUtil.nextDouble() < this.probabilityPunish) {
							double magnitude = ((DenounceSanction) sanction).getMagnitude();
							PunishmentInformationSanction punishment = new PunishmentInformationSanction(
									this.id, prosumerId, this.punishment * magnitude);
							this.sanctions.add(punishment);
						}
					}
				}
			}
		}
		
		return this.sanctions;
	}
	
	
	@Override
	public void generateOutput() {
		
		for(SanctionAbstract sanction : this.sanctions) {
			AbstractEntity entity = (RegulatorOutputEntity) OutputController
					.getEntity(EntityType.REGULATOR);
			
			entity.setValue(Field.CYCLE.name(), this.timestep);
			entity.setValue(Field.SANCTIONED.name(), sanction.getReceiver());
			entity.setValue(Field.SANCTION_TYPE.name(), sanction.getType().name());
			
			switch(sanction.getType()) {
				case PUNISHMENT:
					entity.setValue(Field.PUNISHMENT.name(),
							((PunishmentSanction) sanction).getPunishment());
					entity.setValue(Field.SUSPENSION.name(), 0);
					break;
				case PUNISHMENT_INFORMATION:
					entity.setValue(Field.PUNISHMENT.name(),
							((PunishmentInformationSanction) sanction).getPunishment());
					entity.setValue(Field.SUSPENSION.name(), 0);
					break;
				case SUSPENSION:
					entity.setValue(Field.PUNISHMENT.name(), 0.0);
					entity.setValue(Field.SUSPENSION.name(),
							((SuspensionSanction) sanction).getSuspensionPeriod());
				default:
					break;
			}
			entity.setActive();
			
			OutputController.setEntity(EntityType.REGULATOR, entity);
		}
	}
}